/*
 * The MIT License
 *
 * Copyright 2012 http://generic-toolset.googlecode.com.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.rlpw.lang;

import java.awt.Color;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Set;
import java.util.StringTokenizer;

/**
 *
 */
public class StringParser {

    private static HashMap<Class, Parser> map;

    static {
        map = new HashMap<>();

        for (Class parserClass : StringParser.class.getDeclaredClasses()) {

            if (parserClass.isAnnotationPresent(ParsableClasses.class)) {
                ParsableClasses pc = (ParsableClasses) parserClass.getAnnotation(ParsableClasses.class);
                for (Class c : pc.value()) {
                    try {
                        map.put(c, (Parser) parserClass.newInstance());
                    } catch (InstantiationException | IllegalAccessException ex) {
                    }
                }
            }
        }

    }

    public static <T> T parse(Class<T> c, String str) throws Exception {
        return (T) map.get(c).parse(str);
    }

    public static String getString(Object obj) {

        if (obj == null) {
            return "";
        }

        Class c = obj.getClass();
        if (map.containsKey(c)) {
            return map.get(c).getString(obj);
        }
        return obj.toString();
    }

    public static Set<Class> getParsableTypes() {
        return map.keySet();
    }

    public static boolean isParsable(Class c) {
        return map.containsKey(c);
    }

    private abstract static class Parser<T> {

        abstract T parse(String str) throws Exception;

        String getString(T obj) {
            return obj.toString();
        }
    }

    @ParsableClasses(String.class)
    static class StrParser extends Parser<String> {

        @Override
        String parse(String str) throws Exception {
            return str;
        }
    }

    @ParsableClasses({Boolean.class, boolean.class})
    static class BooleanParser extends Parser<Boolean> {

        @Override
        Boolean parse(String str) throws Exception {
            return Boolean.parseBoolean(str);
        }
    }

    @ParsableClasses({Integer.class, int.class})
    static class IntegerParser extends Parser<Integer> {

        @Override
        Integer parse(String str) throws Exception {
            return Integer.parseInt(str);
        }
    }

    @ParsableClasses({Float.class, float.class})
    static class FloatParser extends Parser<Float> {

        @Override
        Float parse(String str) throws Exception {
            return Float.parseFloat(str);
        }
    }

    @ParsableClasses({Double.class, double.class})
    static class DoubleParser extends Parser<Double> {

        @Override
        Double parse(String str) throws Exception {
            return Double.parseDouble(str);
        }
    }

    @ParsableClasses({Byte.class, byte.class})
    static class ByteParser extends Parser<Byte> {

        @Override
        Byte parse(String str) throws Exception {
            return Byte.parseByte(str);
        }
    }

    @ParsableClasses({Long.class, long.class})
    static class LongParser extends Parser<Long> {

        @Override
        Long parse(String str) throws Exception {
            return Long.parseLong(str);
        }
    }

    @ParsableClasses(Class.class)
    static class ClassParser extends Parser<Class> {

        @Override
        Class parse(String str) throws Exception {
            return Class.forName(str);
        }

        @Override
        String getString(Class c) {
            return c.getName();
        }
    }

    @ParsableClasses(Color.class)
    static class ColorParser extends Parser<Color> {

        private static HashMap<String, Color> map = new HashMap<>();

        static {

            // Vordefinierte Farben aus java.awt.Color rausholen
            for (Field f : Color.class.getFields()) {

                if (f.getType().equals(Color.class)) {
                    try {
                        map.put(f.getName(), (Color) f.get(null));
                    } catch (IllegalArgumentException | IllegalAccessException ex) {
                    }
                }
            }
        }

        @Override
        Color parse(String str) throws Exception {
            if (map.containsKey(str)) {
                return map.get(str);
            } else {
                return Color.decode(str);
            }
        }

        @Override
        String getString(Color c) {

            if (map.containsValue(c)) {
                for (String name : map.keySet()) {
                    if (c.equals(map.get(name))) {
                        return name;
                    }
                }
            }

            return "#" + Integer.toHexString(c.getRGB()).toUpperCase().substring(2);
        }
    }

    @ParsableClasses(Date.class)
    static class DateParser extends Parser<Date> {

        @Override
        Date parse(String str) throws Exception {

            try {
                long ms = StringParser.parse(long.class, str);
                return new Date(ms);
            } catch (Exception ex) {
            }

            StringTokenizer st = new StringTokenizer(str, "[");
            str = st.nextToken();

            SimpleDateFormat dateFormat = new SimpleDateFormat(new StringTokenizer(st.nextToken(), "]").nextToken());
            return dateFormat.parse(str);
        }

        @Override
        String getString(Date d) {
            return "" + d.getTime();
        }
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    private @interface ParsableClasses {

        Class[] value();
    }
}
